import { Component, OnInit, ViewChild } from '@angular/core';
import { MatMenuTrigger } from '@angular/material';
import { trigger, state, style, animate, transition } from '@angular/animations';
import { ActivatedRoute, Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';

import { Examples } from './example-schemas.model';
import { JsonPointer } from 'angular6-json-schema-form';

import { routingPathConfig } from '@config/routing-path.config';
import { RoutingPathPipe } from '@notadd/pipes/routing-path.pipe';

@Component({
    selector: 'json-schema-form-material',
    templateUrl: './json-schema-form.component.html',
    styleUrls: ['./json-schema-form.component.scss'],
    animations: [
        trigger('expandSection', [
            state('in', style({ height: '*' })),
            transition(':enter', [
                style({ height: 0 }), animate(100),
            ]),
            transition(':leave', [
                style({ height: '*' }),
                animate(100, style({ height: 0 })),
            ]),
        ]),
    ],
    providers: [
        RoutingPathPipe
    ]
})
export class JsonSchemaFormComponent implements OnInit {
    examples: any = Examples;
    selectedSet = 'ng-jsf';
    selectedSetName = '';
    selectedExample = 'ng-jsf-flex-layout';
    selectedExampleName = 'Flexbox layout';
    selectedFramework = 'material-design';
    selectedLanguage = 'en';
    visible = {
        options: true,
        schema: true,
        form: true,
        output: true
    };

    formActive = false;
    jsonFormSchema: string;
    jsonFormValid = false;
    jsonFormStatusMessage = 'Loading form...';
    jsonFormObject: any;
    jsonFormOptions: any = {
        addSubmit: true, // Add a submit button if layout does not have one
        debug: false, // Don't show inline debugging information
        loadExternalAssets: true, // Load external css and JavaScript for frameworks
        returnEmptyFields: false, // Don't return values for empty input fields
        setSchemaDefaults: true, // Always use schema defaults for empty fields
        defautWidgetOptions: { feedback: true }, // Show inline feedback icons
    };
    liveFormData: any = {};
    formValidationErrors: any;
    formIsValid = void (0);
    submittedFormData: any = void (0);
    aceEditorOptions: any = {
        highlightActiveLine: true,
        maxLines: 1000,
        printMargin: false,
        autoScrollEditorIntoView: true,
    };
    @ViewChild(MatMenuTrigger) menuTrigger: MatMenuTrigger;

    constructor(
        private http: HttpClient,
        private route: ActivatedRoute,
        private router: Router,
        private path: RoutingPathPipe
    ) {
    }

    compoundInputObject: any;

    ngOnInit() {
        // Subscribe to query string to detect schema to load
        this.route.queryParams.subscribe(
            params => {
                if (params['set']) {
                    this.selectedSet = params['set'];
                    this.selectedSetName = ({
                        'ng-jsf': '',
                        'asf': 'Angular Schema Form:',
                        'rsf': 'React Schema Form:',
                        'jsf': 'JSONForm:'
                    })[this.selectedSet];
                }
                if (params['example']) {
                    this.selectedExample = params['example'];
                    this.selectedExampleName = this.examples[this.selectedSet].schemas
                        .find(schema => schema.file === this.selectedExample).name;
                }
                this.loadSelectedExample();
            }
        );
    }

    onSubmit(data: any) {
        this.submittedFormData = data;
    }

    get prettySubmittedFormData() {
        return JSON.stringify(this.submittedFormData, void (0), 2);
    }

    onChanges(data: any) {
        this.liveFormData = data;
    }

    get prettyLiveFormData() {
        return JSON.stringify(this.liveFormData, void (0), 2);
    }

    isValid(isValid: boolean): void {
        this.formIsValid = isValid;
    }

    validationErrors(data: any): void {
        this.formValidationErrors = data;
    }

    get prettyValidationErrors() {
        if (!this.formValidationErrors) { return void (0); }
        const errorArray = [];
        for (const error of this.formValidationErrors) {
            const message = error.message;
            const dataPathArray = JsonPointer.parse(error.dataPath);
            if (dataPathArray.length) {
                let field = dataPathArray[0];
                for (let i = 1; i < dataPathArray.length; i++) {
                    const key = dataPathArray[i];
                    field += /^\d+$/.test(key) ? `[${key}]` : `.${key}`;
                }
                errorArray.push(`${field}: ${message}`);
            } else {
                errorArray.push(message);
            }
        }
        return errorArray.join('<br>');
    }

    loadSelectedExample(
        selectedSet: string = this.selectedSet,
        selectedSetName: string = this.selectedSetName,
        selectedExample: string = this.selectedExample,
        selectedExampleName: string = this.selectedExampleName
    ) {
        if (this.menuTrigger.menuOpen) { this.menuTrigger.closeMenu(); }
        if (selectedExample !== this.selectedExample) {
            this.formActive = false;
            this.selectedSet = selectedSet;
            this.selectedSetName = selectedSetName;
            this.selectedExample = selectedExample;
            this.selectedExampleName = selectedExampleName;
            this.router.navigateByUrl(
                `${this.path.transform([
                    routingPathConfig.app.elements,
                    routingPathConfig.elements.advancedUi,
                    routingPathConfig.advancedUi.jsonSchemaForm
                ])}` +
                '?set=' + selectedSet +
                '&example=' + selectedExample +
                '&framework=' + this.selectedFramework +
                '&language=' + this.selectedLanguage
            );
            this.liveFormData = {};
            this.submittedFormData = void (0);
            this.formIsValid = void (0);
            this.formValidationErrors = void (0);
        }
        const exampleURL = `assets/example-schemas/${this.selectedExample}.json`;
        this.http
            .get(exampleURL, { responseType: 'text' })
            .subscribe(schema => {
                this.jsonFormSchema = schema;
                this.generateForm(this.jsonFormSchema);
            });
    }

    loadSelectedLanguage() {
        window.location.href =
            `${this.path.transform([
                routingPathConfig.app.elements,
                routingPathConfig.elements.advancedUi,
                routingPathConfig.advancedUi.jsonSchemaForm
            ])}` +
            '?set=' + this.selectedSet +
            '&example=' + this.selectedExample +
            '&framework=' + this.selectedFramework +
            '&language=' + this.selectedLanguage;
    }

    // Display the form entered by the user
    // (runs whenever the user changes the jsonform object in the ACE input field)
    generateForm(newFormString: string) {
        if (!newFormString) { return; }
        this.jsonFormStatusMessage = 'Loading form...';
        this.formActive = false;
        this.liveFormData = {};
        this.submittedFormData = void (0);

        // Most examples should be written in pure JSON,
        // but if an example schema includes a function,
        // it will be compiled it as Javascript instead
        try {

            // Parse entered content as JSON
            this.jsonFormObject = JSON.parse(newFormString);
            this.jsonFormValid = true;
        } catch (jsonError) {
            try {

                // If entered content is not valid JSON,
                // parse as JavaScript instead to include functions
                const newFormObject: any = void (0);
                /* tslint:disable */
                eval('newFormObject = ' + newFormString);
                /* tslint:enable */
                this.jsonFormObject = newFormObject;
                this.jsonFormValid = true;
            } catch (javascriptError) {

                // If entered content is not valid JSON or JavaScript, show error
                this.jsonFormValid = false;
                this.jsonFormStatusMessage =
                    'Entered content is not currently a valid JSON Form object.\n' +
                    'As soon as it is, you will see your form here. So keep typing. :-)\n\n' +
                    'JavaScript parser returned:\n\n' + jsonError;
                return;
            }
        }
        this.formActive = true;
    }

    toggleVisible(item: string) {
        this.visible[item] = !this.visible[item];
    }

    toggleFormOption(option: string) {
        if (option === 'feedback') {
            this.jsonFormOptions.defautWidgetOptions.feedback =
                !this.jsonFormOptions.defautWidgetOptions.feedback;
        } else {
            this.jsonFormOptions[option] = !this.jsonFormOptions[option];
        }
        this.generateForm(this.jsonFormSchema);
    }
}
